/*!
\copyright  Copyright (c) 2020 - 2023 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file
    \ingroup    le_unicast_manager
\brief     Implementation of the voice source interface for LE Voice sources.
*/

#if defined(INCLUDE_LE_AUDIO_UNICAST)

#include "le_unicast_voice_param.h"
#include "le_unicast_voice_source.h"
#include "le_unicast_manager_instance.h"
#include "le_unicast_telephony_control.h"
#include "kymera_adaptation.h"
#include "kymera_adaptation_voice_protected.h"
#include "audio_router_connect.h"
#include "call_control_client.h"
#include <logging.h>

static bool leUnicastVoiceSource_IsVoiceChannelAvailable(voice_source_t source);
static bool leUnicastVoiceSource_IsAudioRouted(voice_source_t source);
static source_status_t leUnicastVoiceSource_SetState(voice_source_t source, source_state_t state);

static const voice_source_audio_interface_t voice_source_audio_interface =
{
    .GetConnectParameters = LeUnicastVoiceSource_GetAudioConnectParameters,
    .ReleaseConnectParameters = LeUnicastVoiceSource_FreeAudioConnectParameters,
    .GetDisconnectParameters = LeUnicastVoiceSource_GetAudioDisconnectParameters,
    .ReleaseDisconnectParameters = LeUnicastVoiceSource_FreeAudioDisconnectParameters,
    .IsAudioRouted = leUnicastVoiceSource_IsAudioRouted,
    .IsVoiceChannelAvailable = leUnicastVoiceSource_IsVoiceChannelAvailable,
    .SetState = leUnicastVoiceSource_SetState,
    .GetState = LeUnicastVoiceSource_GetState
};

void LeUnicastVoiceSource_Reconfig(le_um_instance_t *inst)
{
    bool status = FALSE;

    generic_source_t source =
    {
        .type = source_type_voice,
        .u =
        {
            .voice = LeUnicastManager_GetVoiceSourceForInstance(inst)
        }
    };

    AudioRouter_DisconnectVoiceSource(source);
    status = AudioRouter_ConnectVoiceSource(source);

    UNICAST_MANAGER_LOG("LeUnicastVoiceSource_Reconfig: Status %d", status);
}

bool LeUnicastVoiceSource_IsVoiceChannelAvailable(voice_source_t source)
{
    bool voice_available = FALSE;
    le_um_instance_t *inst = LeUnicastManager_InstanceGetByVoiceSource(source);

    if (inst)
    {
        if (LeUnicastManager_IsContextTypeConversational(inst->audio_context) &&
            LeUnicastManager_IsAnyAseEnabled(inst))
        {
            voice_available = TRUE;
        }
    }

    UNICAST_MANAGER_LOG("LeUnicastVoiceSource_IsVoiceChannelAvailable source enum:voice_source_t:%u is_available %d",
                        source, voice_available);

    return voice_available;
}

static bool leUnicastVoiceSource_IsVoiceChannelAvailable(voice_source_t source)
{

    return LeUnicastVoiceSource_IsVoiceChannelAvailable(source);
}

bool LeUnicastVoiceSource_IsAudioRouted(voice_source_t source)
{
    bool is_routed = FALSE;

    le_um_instance_t *inst = LeUnicastManager_InstanceGetByVoiceSource(source);

    if (inst)
    {
        if (LeUnicastManager_IsContextTypeConversational(inst->audio_context) &&
            LeUnicastManager_IsAnyAseEnabled(inst))
        {
            is_routed = TRUE;
        }

        DEBUG_LOG("LeUnicastVoiceSource_IsAudioRouted is_routed %d is_conversational %d is_any_ase_enabled %d",
                  is_routed,
                  LeUnicastManager_IsContextTypeConversational(inst->audio_context),
                  LeUnicastManager_IsAnyAseEnabled(inst));
    }

    return is_routed;
}

static bool leUnicastVoiceSource_IsAudioRouted(voice_source_t source)
{


    return LeUnicastVoiceSource_IsAudioRouted(source);
}

static source_status_t leUnicastVoiceSource_SetState(voice_source_t source, source_state_t state)
{
    source_status_t source_status = source_status_ready;
    le_um_instance_t *inst = LeUnicastManager_InstanceGetByVoiceSource(source);
    generic_source_t gen_source;

    gen_source.type = source_type_voice;
    gen_source.u.voice = source;

    UNICAST_MANAGER_LOG("leUnicastVoiceSource_SetState source enum:voice_source_t:%u state enum:source_state_t:%u", source, state);

    if (inst != NULL)
    {
        switch (state)
        {
            case source_state_disconnecting:
            {
                source_status = leUnicastManager_HandleSourceInDisconnectingState(inst, gen_source);
            }
            break;

            case source_state_disconnected:
            {
#ifdef ENABLE_LE_MULTIPOINT_BACKGROUND_CIS_SUPPORT
                if (inst->source_state == source_state_invalid)
                {
                    /* This source got added into audio router, but did not got focus, because of some high priority
                     * source already running. Goahead and push this for background processing.
                     */
                    source_status = leUnicastManager_StartUnicastSession(inst, FALSE, gen_source);
                }
                else
#endif
                {
                    /* End the current unicast voice session */
                    leUnicastManager_EndUnicastSession(inst);
                }
            }
            break;

            case source_state_connecting:
            {
                /* Ensure we don't call start session again when one is already in progress */
                if (!LeUnicastManager_IsInstanceReadyForStreaming(inst))
                {
                    source_status = leUnicastManager_StartUnicastSession(inst, TRUE, gen_source);
                }
            }
            break;

#ifdef ENABLE_LE_MULTIPOINT_BACKGROUND_CIS_SUPPORT
            case source_state_connected:
            {
                /* There are cases when there is an incoming call from AG playing in band ringtone and other high priority
                 * voice source with (Active call/ Outgoing) gets focus and the source playing in band ringtone goes into background,
                 * due to which there is no indication for incoming call.
                 * Play an out of band ringtone (if there is an incoming call) for the other voice source which is not in focus.
                 */
                CallControlClient_PlayOutOfBandRingtone((source == voice_source_le_audio_unicast_1) ? 
                                        voice_source_le_audio_unicast_2 : voice_source_le_audio_unicast_1);
            }
#endif

            default:
            break;
        }

        inst->source_state = state;
    }

    return source_status;
}


source_state_t LeUnicastVoiceSource_GetState(voice_source_t source)
{
    source_state_t state = source_state_invalid;
    le_um_instance_t *inst = LeUnicastManager_InstanceGetByVoiceSource(source);

    if (inst != NULL)
    {
        state =  inst->source_state;
    }

    UNICAST_MANAGER_LOG("leUnicastVoiceSource_GetState: source=enum:voice_source_t:%d, state=enum:source_state_t:%d", source, state);

    return state;
}

const voice_source_audio_interface_t * LeUnicastVoiceSource_GetAudioInterface(void)
{
    return &voice_source_audio_interface;
}

void LeUnicastVoiceSource_Init(void)
{
    /* Register with audio source for Audio use case */
    VoiceSources_RegisterAudioInterface(voice_source_le_audio_unicast_1,
                                        LeUnicastVoiceSource_GetAudioInterface());

    VoiceSources_RegisterTelephonyControlInterface(voice_source_le_audio_unicast_1,
                                                   LeVoiceSource_GetTelephonyControlInterface());

    /* Register with audio source for Audio use case */
    VoiceSources_RegisterAudioInterface(voice_source_le_audio_unicast_2,
                                        LeUnicastVoiceSource_GetAudioInterface());

    VoiceSources_RegisterTelephonyControlInterface(voice_source_le_audio_unicast_2,
                                                   LeVoiceSource_GetTelephonyControlInterface());
}

#endif /* defined(INCLUDE_LE_AUDIO_UNICAST) */
